/**
 * 
 */
package datamodel.tree.linked;

import datamodel.tree.Tree;
import datamodel.tree.TreeNode;

/**
 * An implementation of the tree interface.<br>
 * The nodes are Linked to each other.
 * 
 * @author Kevin Munk, Jakob Karolus
 * @version 1.0
 * 
 */
public class LinkedTree extends Tree {
	
	LinkedTreeNode currentNode = null;
	LinkedTreeNode root = null;

	

	/**
	 * Moves to the left child of the current node
	 * 
	 * @return true if left child exists and the move was successful; otherwise
	 *         false
	 */
	public boolean moveToLeftNode() {
		return moveTo("LEFT");
	}

	/**
	 * Moves to the right child of the current node
	 * 
	 * @return true if right child exists and the move was successful; otherwise
	 *         false
	 */
	public boolean moveToRightNode() {
		return moveTo("RIGHT");
	}

	/**
	 * Moves to the parent of the current node
	 * 
	 * @return true if parent exists and the move was successful; otherwise
	 *         false
	 */
	public boolean moveToParentNode() {
		return moveTo("PARENT");
	}

	/**
	 * @return true if left child exists; otherwise false
	 */
	public boolean hasLeftNode() {
		if(currentNode == null)
			return false;
		else
			return currentNode.hasLeftChild();
	}

	/**
	 * @return true if right child exists; otherwise false
	 */
	public boolean hasRightNode() {
		if(currentNode == null)
			return false;
		else
			return currentNode.hasRightChild();
	}

	/**
	 * @return true if parent exists; otherwise false
	 */
	public boolean hasParentNode() {
		if(currentNode == null)
			return false;
		else
			return currentNode.hasParent();
	}

	/**
	 * Sets the left child of the current node
	 * 
	 * @return true if successful; otherwise false (no root set)
	 * 
	 */
	public boolean setLeftNode(TreeNode node) {
		return setNode("LEFT", node);
	}

	/**
	 * Sets the right child of the current node
	 * 
	 * @return true if successful; otherwise false (no root set)
	 * 
	 */
	public boolean setRightNode(TreeNode node) {
		return setNode("RIGHT", node);
	}

	/**
	 * Sets the current node. If the tree is empty, sets the root.
	 * 
	 */
	public void setCurrentNode(TreeNode node) {
		setNode("CURRENT", node);
	}

	/**
	 * @return the current node or null if the tree is empty
	 */
	public TreeNode getCurrentNode() {
		if(isEmpty())
			return null;
		else
			return currentNode;
	}

	/**
	 * moves to the root node of this tree
	 * 
	 * @return true if there's a root; otherwise false
	 */
	public boolean moveToRoot() {
		currentNode = root;
		return (root != null);
	}

	/**
	 * clears the whole tree, which includes deleting all nodes and the root
	 * node
	 */
	public void clearTree() {
		root = null;
		currentNode = null;
	}
	
	// HELPERS BEGIN HERE
	
	
	private boolean setNode(String nodeType, TreeNode tn) throws IllegalArgumentException {
		// TreeNode must be a LinkedTreeNode
		if(!(tn instanceof LinkedTreeNode))
			return false;
		
		// Cast
		LinkedTreeNode ltn = (LinkedTreeNode)tn;
		
		// setLeftNode() was called.
		if(nodeType.equals("LEFT")) {
			if(currentNode == null)
				return false;
			// else:
			currentNode.setLeftChild(ltn);
			ltn.setParent(currentNode);
			return true;
		}
		// setRightNode() was called.
		else if(nodeType.equals("RIGHT")) {
			if(currentNode == null)
				return false;
			// else:
			currentNode.setRightChild(ltn);
			ltn.setParent(currentNode);
			return true;
		}
		// setCurrentNode() was called.
		else if(nodeType.equals("CURRENT")) {
			// Tree empty? -> Set root
			if(isEmpty()) {
				root = ltn;
				currentNode = root;
				return true;
			}
			// else: set current node
			LinkedTreeNode lc = currentNode.getLeftChild(),
			  			   rc = currentNode.getRightChild();

			currentNode = ltn;
			
			currentNode.setLeftChild(lc);
			currentNode.setRightChild(rc);
			lc.setParent(currentNode);
			rc.setParent(currentNode);
			
			return true;
		}
		// Unknown nodeType
		else
			// Handle typing errors...
			throw new IllegalArgumentException("Unknown node type");
		
	}
	
	private boolean moveTo(String direction) throws IllegalArgumentException {
		boolean hasChildOrParent = false;
		LinkedTreeNode ltn = null;
		
		if(direction.equals("LEFT")) {
			if(currentNode == null)
				return false;
			// else:
			hasChildOrParent = currentNode.hasLeftChild();
			ltn = currentNode.getLeftChild(); // Might be null.
		}
		else if(direction.equals("RIGHT")) {
			if(currentNode == null)
				return false;
			// else:
			hasChildOrParent = currentNode.hasRightChild();
			ltn = currentNode.getRightChild();  // Might be null.
		}
		else if(direction.equals("PARENT")) {
			if(currentNode == null)
				return false;
			// else:
			hasChildOrParent = currentNode.hasParent();
			ltn = currentNode.getParent(); // Might be null.
		}
		else
			// Handle typing errors...
			throw new IllegalArgumentException("Unknown direction");
		
		// Only assign if hasChildOrParent==true
		if(hasChildOrParent) { // avoid assigning if child/parent does not exist.
			currentNode = ltn;
			return true;
		}
		else // ASSUMPTION: No movement if child or parent don't exist.
			// 				Otherwise, null would be assigned.
			return false;
	}
	
	private boolean isEmpty() {
		return (root == null) && (currentNode == null);
	}

}
